msg_tool\scripts\bgi\archive/
bse.rs

1use crate::ext::io::*;
2use crate::scripts::base::*;
3use crate::types::*;
4use anyhow::Result;
5use std::io::{Read, Seek};
6
7pub trait BseGenerator {
8    fn next_key(&mut self) -> i32;
9}
10
11pub struct BseGenerator100 {
12    key: i32,
13}
14
15impl BseGenerator100 {
16    pub fn new(key: i32) -> Self {
17        BseGenerator100 { key }
18    }
19}
20
21impl BseGenerator for BseGenerator100 {
22    fn next_key(&mut self) -> i32 {
23        let key = (self
24            .key
25            .overflowing_mul(257)
26            .0
27            .overflowing_shr(8)
28            .0
29            .overflowing_add(self.key.overflowing_mul(97).0)
30            .0
31            .overflowing_add(23)
32            .0) as u32
33            ^ 0xA6CD9B75;
34        self.key = key.rotate_right(16) as i32;
35        self.key
36    }
37}
38
39pub struct BseGenerator101 {
40    key: i32,
41}
42
43impl BseGenerator101 {
44    pub fn new(key: i32) -> Self {
45        BseGenerator101 { key }
46    }
47}
48
49impl BseGenerator for BseGenerator101 {
50    fn next_key(&mut self) -> i32 {
51        let key = (self
52            .key
53            .overflowing_mul(127)
54            .0
55            .overflowing_shr(7)
56            .0
57            .overflowing_add(self.key.overflowing_mul(83).0)
58            .0
59            .overflowing_add(53)
60            .0) as u32
61            ^ 0xB97A7E5C;
62        self.key = key.rotate_right(16) as i32;
63        self.key
64    }
65}
66
67pub struct BseReader<T: Read + Seek, F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> {
68    reader: T,
69    header: [u8; 0x40],
70    detect: F,
71    pos: u64,
72    filename: String,
73}
74
75impl<T: Read + Seek, F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> BseReader<T, F> {
76    pub fn new(mut reader: T, detect: F, filename: &str) -> Result<Self> {
77        reader.seek(std::io::SeekFrom::Start(8))?;
78        let version = reader.read_u16()?;
79        if version != 0x0100 && version != 0x0101 {
80            return Err(anyhow::anyhow!("Unsupported BSE version: {}", version));
81        }
82        let _checksum = reader.read_u16()?;
83        let key = reader.read_i32()?;
84        let mut header = [0u8; 0x40];
85        reader.read_exact(&mut header)?;
86        let generator: Box<dyn BseGenerator> = if version == 0x0100 {
87            Box::new(BseGenerator100::new(key))
88        } else {
89            Box::new(BseGenerator101::new(key))
90        };
91        Self::decode_header(&mut header, generator)?;
92        Ok(BseReader {
93            reader,
94            header,
95            detect,
96            pos: 0,
97            filename: filename.to_string(),
98        })
99    }
100
101    fn decode_header(data: &mut [u8; 0x40], mut generator: Box<dyn BseGenerator>) -> Result<()> {
102        let mut decoded = [false; 0x40];
103        for _ in 0..0x40 {
104            let mut dst = (generator.next_key() & 0x3F) as usize;
105            while decoded[dst] {
106                dst = (dst + 1) & 0x3F;
107            }
108            let shift = generator.next_key() & 7;
109            let right_shift = (generator.next_key() & 1) == 0;
110            let key_byte = generator.next_key();
111            let symbol = (data[dst] as i32).wrapping_sub(key_byte);
112            let symbol = symbol as u8;
113            data[dst] = if right_shift {
114                symbol.rotate_right(shift as u32)
115            } else {
116                symbol.rotate_left(shift as u32)
117            };
118            decoded[dst] = true;
119        }
120        Ok(())
121    }
122
123    pub fn is_dsc(&self) -> bool {
124        self.header.starts_with(b"DSC FORMAT 1.00")
125    }
126}
127
128impl<T: Read + Seek, F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> Read
129    for BseReader<T, F>
130{
131    fn read(&mut self, buf: &mut [u8]) -> std::io::Result<usize> {
132        if self.pos < 0x40 {
133            let bytes_to_read = (0x40 - self.pos).min(buf.len() as u64);
134            buf[..bytes_to_read as usize].copy_from_slice(
135                &self.header[self.pos as usize..self.pos as usize + bytes_to_read as usize],
136            );
137            self.pos += bytes_to_read;
138            Ok(bytes_to_read as usize)
139        } else {
140            let true_pos = self.pos + 0x10;
141            self.reader.seek(std::io::SeekFrom::Start(true_pos))?;
142            let bytes_read = self.reader.read(buf)?;
143            self.pos += bytes_read as u64;
144            Ok(bytes_read)
145        }
146    }
147}
148
149impl<T: Read + Seek, F: Fn(&[u8], usize, &str) -> Option<&'static ScriptType>> ArchiveContent
150    for BseReader<T, F>
151{
152    fn name(&self) -> &str {
153        &self.filename
154    }
155
156    fn script_type(&self) -> Option<&ScriptType> {
157        (self.detect)(&self.header, self.header.len(), &self.filename)
158    }
159}